﻿// 游戏状态
const GameStatus = {
    END: 0,     // 已结束
    PAUSED: 1,  // 暂停中
    PLAYING: 2  // 游戏中
}

/**
 * 游戏类的构造方法。
 * @param drawer    绘图者
 * @param gameBoard 游戏区域
 */
function Game(drawer, gameBoard) {
    var mApple = null                                               // 果子
    var mDifficulty = 1                                             // 难度
    const mDifficultyUpEvent = document.createEvent("customEvent")  // 难度增加事件
    const mDrawer = drawer                                          // 绘图者
    var mEnableMusic = false                                        // 音乐开关
    const mGameBoard = gameBoard                                    // 游戏区域
    const mGameOverEvent = document.createEvent("customEvent")      // 游戏结束事件
    const mMusicPlayer = document.getElementById("IDC_MUSIC")       // 音乐播放者
    var mScore = 0                                                  // 得分
    const mScoreEvent = document.createEvent("customEvent")         // 得分事件
    var mSnake = null                                               // 蛇
    var mStatus = GameStatus.END                                    // 状态
    var mTimer = null                                               // 定时器

    // 初始化成员
    mDifficultyUpEvent.initEvent(Const.UM_DIFFICULTY_UP, false, false)
    mScoreEvent.initEvent(Const.UM_SCORE, false, false)
    mGameOverEvent.initEvent(Const.UM_GAME_OVER, false, false)

    // 重绘游戏区域
    drawGameBoard()
    mGameBoard.repaint()

    /**
     * 在随机位置创建果子。
     */
    function createApple() {
        var point = new Point(0, 0)
        var used = false

        // 创建果子
        do {
            // 取 x、y 的随机值
            point.x = (Math.floor(Math.random() * 10) % (Const.GAME_BOARD_GRID_COUNT_H - 2) + 1)
            point.y = (Math.floor(Math.random() * 10) % (Const.GAME_BOARD_GRID_COUNT_V - 2) + 1)
            used = pointIsUsed(point)
        } while (used)
        mApple = new SnakeBody(mDrawer, point, SnakeBodyType.APPLE, Const.APPLE_COLOR)
    }

    /**
     * 画游戏区域。
     */
    function drawGameBoard() {
        mDrawer.fillStyle = Const.GAME_BOARD_COLOR
        mDrawer.fillRect(0, 0, Const.GAME_BOARD_WIDTH, Const.GAME_BOARD_HEIGHT)
    }

    /**
     * 游戏结束。
     * @param isWon  胜利则为 true，否则为 false
     */
    function gameOver(isWon) {
        // 将状态设置为已结束
        mStatus = GameStatus.END

        // 停止定时器
        window.clearInterval(mTimer)

        // 停止播放音乐
        if (mEnableMusic) {
            mMusicPlayer.pause()
            mMusicPlayer.load()
        }

        // 发送通知
        mGameOverEvent.arg1 = isWon
        document.dispatchEvent(mGameOverEvent)
    }

    /**
     * 获取游戏状态。
     * @return 游戏状态
     */
    this.getStatus = function() {
        return mStatus
    }

    /**
     * 判断是否胜利。
     * @return 胜利则返回 true，否则返回 false
     */
    function isWon() {
        return mScore >= Const.WIN_NUMBER
    }

    /**
     * 按键事件的响应方法。
     * @param key   按键
     */
    this.onKeyPressed = function(key) {
        if (mStatus != GameStatus.PLAYING) {
            return
        }
        if (key >= Const.KEY_LEFT && key <= Const.KEY_DOWN) {
            mSnake.turn(key)
        }
    }

    /**
     * 定时器事件的响应方法。
     * @param e 事件相关信息
     */
    function onTimer(e) {
        // 移动蛇
        var isDead = mSnake.move()
        if (isDead) {
            gameOver(false)
            return
        }

        // 检查是否碰到果子
        const head = mSnake.getHead()
        if (head.getPosition().equals(mApple.getPosition())) {
            // 碰到果子，将果子加入蛇链表
            mSnake.eat(mApple)
            createApple()
            mScoreEvent.arg1 = ++mScore
            document.dispatchEvent(mScoreEvent)

            // 判断是否胜利
            if (isWon()) {
                gameOver(true)
                return
            }

            // 判断是否达到增加难度的条件
            if (mScore % Const.DIFF_BASE == 0 && mDifficulty != Const.DIFF_MAX) {
                mDifficultyUpEvent.arg1 = ++mDifficulty
                document.dispatchEvent(mDifficultyUpEvent)
                window.clearInterval(mTimer)
                var elapse = Const.TIMER_INIT - Const.TIMER_GROW * (mDifficulty - 1)
                mTimer = window.setInterval(onTimer, elapse)
            }
        }

        // 通知游戏区域重绘
        mGameBoard.repaint()
    }

    /**
     * 游戏暂停。
     */
    this.pause = function() {
        if (mStatus == GameStatus.PLAYING) {
            // 停止定时器
            window.clearInterval(mTimer)

            // 停止播放音乐
            if (mEnableMusic) {
                mMusicPlayer.pause()
            }

            // 将状态设置为已暂停
            mStatus = GameStatus.PAUSED
        } else if (mStatus == GameStatus.PAUSED) {
            // 开启定时器
            mTimer = window.setInterval(onTimer, Const.TIMER_INIT - Const.TIMER_GROW * (mDifficulty - 1))

            // 播放音乐
            if (mEnableMusic) {
                mMusicPlayer.play()
            }

            // 将状态设置为游戏中
            mStatus = GameStatus.PLAYING
        }
    }

    /**
     * 播放音乐。
     * @return 正在播放音乐则为 true，否则为 false
     */
    this.playMusic = function() {
        mEnableMusic = !mEnableMusic

        if (mEnableMusic) {
            if (mStatus == GameStatus.PLAYING) {
                mMusicPlayer.play()
            }
        } else {
            mMusicPlayer.pause()
        }

        return mEnableMusic
    }
    
    /**
     * 判断某个点是否被占用。
     * @param point 点
     * @return 被占用了则返回 true，否则返回 false
     */
    function pointIsUsed(point) {
        // 检查点是否在蛇上
        if (mSnake != null) {
            if (mSnake.pointIsOnMe(point)) {
                return true
            }
        }

        // 检查点是否在果子上
        if (mApple != null) {
            return mApple.getPosition().equals(point)
        }

        return false
    }

    /**
     * 游戏开始。
     */
    this.start = function() {
        // 重绘游戏区域
        drawGameBoard()

        // 创建蛇
        mSnake = new Snake(mDrawer)

        // 创建果子
        createApple()

        // 通知游戏区域重绘
        mGameBoard.repaint()

        // 重置难度
        mDifficulty = 1

        // 重置分数
        mScore = 0

        // 开启定时器
        if (mTimer != null) {
            window.clearInterval(mTimer)
        }
        mTimer = window.setInterval(onTimer, Const.TIMER_INIT)

        // 播放音乐
        if (mEnableMusic) {
            mMusicPlayer.play()
        }
        
        // 将状态设置为游戏中
        mStatus = GameStatus.PLAYING
    }
}
